home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / lsof_3.37 / 00PORTING < prev    next >
Encoding:
Text File  |  1995-08-01  |  22.4 KB  |  573 lines

  1.  
  2.         Guide to Porting lsof 3 to Unix OS Dialects
  3.  
  4. **********************************************************************
  5. | The latest release of lsof is always available via anonymous ftp   |
  6. | from vic.cc.purdue.edu.  Look in pub/lsof.README for its location. |
  7. **********************************************************************
  8.  
  9.                 Contents
  10.  
  11.     General Guidelines
  12.     Organization
  13.     Source File Naming Conventions
  14.     Coding Philosophies
  15.     Data Requirements
  16.     Dlsof.h and #include's
  17.     Definitions That Affect Compilation
  18.     Options: Common and Special
  19.     Defining Dialect-Specific Symbols and Global Storage
  20.     Coding Dialect-specific Functions
  21.     Function Prototype Definitions and the _PROTOTYPE Macro
  22.     The Makefile
  23.     The Mksrc Shell Script
  24.  
  25.  
  26. General Guidelines
  27. ------------------
  28.  
  29. These are the general guidelines for porting lsof 3 to a new Unix
  30. dialect:
  31.  
  32.     *  Understand the organization of the lsof sources and the
  33.        philosophies that guide their coding.
  34.  
  35.     *  Understand the data requirements and determine the methods
  36.        of locating the necessary data in the new dialect's kernel.
  37.  
  38.     *  Pick a name for the subdirectory in lsof3/dialects for your
  39.        dialect.
  40.  
  41.     *  Locate the necessary header files and #include them in the
  42.        dialect's dlsof.h file.  (You may not be able to complete
  43.        this step until you have coded all dialect-specific functions.)
  44.  
  45.     *  Determine the optional common functions of lsof to be used
  46.        and set their definitions in the dialect's machine.h file.
  47.  
  48.     *  Define the dialect's specific symbols and global storage
  49.        in the dialect's dlsof.h and dstore.c files.
  50.  
  51.     *  Code the dialect-specific functions in the appropriate
  52.        source files of the dialect's subdirectory.  Select appropriate
  53.        common code fragments from lsof3/dialects/common.
  54.  
  55.        Include the necessary prototype definitions of the dialect-
  56.        specific functions in the dproto.h file in the dialect's
  57.        subdirectory.
  58.  
  59.     *  Define the dialect's Makefile and source construction shell
  60.        script, Mksrc.  Include in Mksrc the steps necessary to
  61.        construct source files from common code fragments.
  62.  
  63. Organization
  64. ------------
  65.  
  66. The code in a dialect-specific version of lsof comes from three
  67. sources:
  68.  
  69.     1)  functions common to all versions, located in the top level
  70.     directory, lsof3;
  71.  
  72.     2)  functions specific to the dialect, located in the dialect's
  73.     subdirectory -- e.g., lsof3/dialects/sun;
  74.  
  75.     3)  functions that are common to several dialects, although
  76.     not to all, contained in code fragment files, located in
  77.     a common subdirectory -- e.g., lsof3/dialects/common/rdev.frag.
  78.  
  79. The tree looks like this:
  80.  
  81.                 lsof3
  82.                 |   \
  83.   1) fully common functions +    \
  84.       e.g., lsof3/main.c      + lsof3/dialects
  85.                / / / / \
  86.                + + + +  \
  87. 2) dialect-specific subdirectories   + 3) common code fragments -- e.g.,
  88.    -- e.g., lsof3/dialects/sun          lsof3/dialects/common/rdev.frag
  89.  
  90. The code for a dialect-specific version is assembled from these
  91. three sources by the Configure shell script in the top level
  92. directory.  It calls on the Mksrc shell script in each dialect's
  93. subdirectory to assemble the dialect-specific sources.  That assembly
  94. can be simply creating a symbolic link from the top level to the
  95. dialect's subdirectory, or it can be a process of combining a
  96. dialect-specific source file with code fragment files from the
  97. lsof3/dialects/common subdirectory.
  98.  
  99. The Configure script completes the dialect's Makefile by adding
  100. string definitions to it while copying it from the dialect's
  101. subdirectory to the top level.
  102.  
  103.  
  104. Source File Naming Conventions
  105. ------------------------------
  106.  
  107. With one exception, dialect-specific source files begin with a
  108. lower case `d' character -- ddev.c, dfile.c, dlsof.h.  The one
  109. exception is the header file that contains dialect-specific
  110. definitions for the optional features of the common functions.
  111. It's called machine.h for historical reasons.
  112.  
  113. Currently all dialects use almost the same source file names.  One
  114. exception to the rule happens in dialects where there must be
  115. different source files -- e.g., dnode[123].c -- to eliminate node
  116. header file structure element name conflicts.  The source modules
  117. in the dcosx and novell subdirectories are organized this way.
  118.  
  119. These are common files in lsof3/:
  120.  
  121.     Configure    the configuration script
  122.     version    the version number
  123.     dialects/    the dialects subdirectory
  124.  
  125. These are the common function source files in lsof3/:
  126.  
  127.     arg.c    common argument processing functions
  128.     lsof.h    common header file that #include's the dialect-specific
  129.         header files
  130.     main.c    common main function for lsof 3
  131.     misc.c    common miscellaneous functions -- e.g., special versions
  132.         of stat() and readlink()
  133.     node.c    common node reading functions -- readinode(), readvnode()
  134.     print.c    common print support functions
  135.     proc.c    common process and file structure functions
  136.     proto.h    common prototype definitions, including the definition of
  137.         the _PROTOTYPE() macro
  138.     store.c    common global storage version.h    the current lsof version
  139.         number, derived from the file version by the Makefile
  140.  
  141. These are the dialect-specific source files:
  142.  
  143.     ddev.c    device support functions -- readdev()
  144.     dfile.c    file processing functions -- commonly this file uses
  145.         code fragments from lsof3/dialects/common
  146.     dlsof.h    dialect-specific header file -- contains #include's
  147.         for system header files and dialect-specific global
  148.         storage declarations
  149.     dmnt.c    mount support functions -- commonly this file uses code
  150.         fragments from lsof3/dialects/common
  151.     dnode.c    node processing functions -- e.g., for gnode or vnode
  152.     dnode?.c    additional node processing functions, used when node
  153.         header files have duplicate and conflicting element
  154.         names.
  155.     dproc.c    functions to access, read, examine and cache data about
  156.         dialect-specific process structures -- this file contains
  157.         the dialect-specific "main" function, gather_proc_info()
  158.     dproto.h    dialect-specific prototype declarations
  159.     dsock.c    dialect-specific socket processing functions
  160.     dstore.c    dialect-specific global storage -- e.g., the nlist()
  161.         structure
  162.     machine.h    dialect specific definitions of common function options --
  163.         e.g., a HASINODE definition to activate the readinode()
  164.         function in lsof3/node.c
  165.  
  166. These are the common code fragments.  The file common/00Manifest
  167. describes the fragments and the dialects that use them.  Note that
  168. some fragments may be configured with #define statements.  Consult
  169. the comments at the beginning of the fragments and check the methods
  170. that existing dialects use to configure them.
  171.  
  172.     ckfa.frag    ck_file_arg() function
  173.     cvfs.frag    completevfs() function
  174.     dvch.frag    read_dcache() and write_dcache() functions for
  175.         handling the device cache file (these functions
  176.         may be configured slightly)
  177.  
  178.         +=================================================+
  179.         | IF YOUR DIALECT MUST BE SETUID(ROOT), MAKE SURE |
  180.         | YOU DEFINE DVCH_CHOWN OR DVCH_FCHOWN.          |
  181.         +=================================================+
  182.  
  183.     isfn.frag    is_file_named() function
  184.     pcdn.frag    printchdevname() function
  185.     prfp.frag    process_file() function
  186.     prtf.frag    print_file() function
  187.     rdev.frag    contains readdev() and stkdir() functions that may
  188.         be configured slightly
  189.     rmnt.frag    readmnt() function
  190.     rnam.frag    BSD-style name cache functions, ncache_*() that may
  191.         be configured slightly
  192.     rnch.frag    SYSV-style name cache functions, ncache_*() that may
  193.         be configured slightly
  194.     rvfs.frag    readvfs() function
  195.  
  196.  
  197. Coding Philosophies
  198. -------------------
  199.  
  200. A few basic philosophies govern the coding of lsof 3 functions:
  201.  
  202.     *  Use as few #if/#else/#endif constructs as possible, even at
  203.        the cost of nearly-duplicate code.
  204.  
  205.        When #if/#else/#endif constructs are necessary:
  206.        
  207.        o  Use the form
  208.  
  209.         #if    defined(s<symbol>)
  210.     
  211.       in preference to
  212.     
  213.         #ifdef    <symbol>
  214.     
  215.       to allow easier addition of tests to the #if.
  216.  
  217.        o  Indent them to signify their level -- e.g.,
  218.  
  219.         #if    /* level one */
  220.         # if    /* level two */
  221.         # endif    /* level two */
  222.         #else    /* level one */
  223.         #endif    /* level one */
  224.  
  225.     o  Use ANSI standard comments on #else and #endif statements.
  226.  
  227.     *  Document copiously.
  228.  
  229.     *  Aim for ANSI-C compatibility:
  230.     
  231.        o  Use function prototypes for all functions, hiding them
  232.       from compilers that cannot handle them with the _PROTOTYPE()
  233.       macro.
  234.  
  235.        o  Use the compiler's ANSI conformance checking wherever
  236.       possible -- e.g., gcc's -ansi option.
  237.  
  238.  
  239. Data Requirements
  240. -----------------
  241.  
  242. Lsof's strategy in obtaining open file information is to access
  243. the process table via its proc structures, then obtain the associated
  244. user area and open file structures.  The open file structures then
  245. lead lsof to file type specific structures -- cdrnodes, fifonodes,
  246. inodes, gnodes, hsfsnodes, pipenodes, pcnodes, rnodes, snodes,
  247. sockets, tmpnodes, and vnodes.
  248.  
  249. This means that to begin an lsof port to a new Unix dialect you
  250. must understand how to obtain these structures from the dialect's
  251. kernel.  Look for kernel access functions -- e.g., the AIX readx()
  252. function, Sun and Sun-like kvm_*() functions, or SGI's syssgi()
  253. function.  Look for clues in header files -- e.g. external declarations
  254. and macroes.
  255.  
  256. If you have access to them, look at sources to programs like ps(1),
  257. or the freely available monitor and top programs.  They may give
  258. you important clues on reading proc and user area structures.  An
  259. appeal to readers of dialect-specific news groups may uncover
  260. correspondents who can help.
  261.  
  262. Careful reading of system header files -- e.g., <sys/proc.h> --
  263. may give hints about how kernel storage is organized.  Look for
  264. global variables declared under a KERNEL or _KERNEL #if.  Run nm(1)
  265. across the kernel image (/vmunix, /unix, etc.) and look for references
  266. to structures of interest.
  267.  
  268. Even if there are support functions for reading structures, like the
  269. kvm_*() functions, you must still understand how to read data from
  270. kernel memory.  Typically this requires an understanding of the
  271. nlist() function, and how to use /dev/kmem, /dev/mem, and /dev/swap.
  272.  
  273. Don't overlook the possibility that you may have to use the process
  274. file system -- e.g., /proc.  Look at the Motorola V/88 R40V4.2 and
  275. Novell UnixWare dialects for examples.  I try to avoid using /proc
  276. when I can, since it usually requires that lsof have setuid(root)
  277. permission to read the individual /proc "files".
  278.  
  279. Once you can access kernel structures, you must understand how
  280. they're connected.  You must answer questions like:
  281.  
  282.     *  How are the proc structures organized?  Is is a static
  283.        table?  Are the proc structures linked?  Is there a
  284.        kernel pointer to the first proc structure?  Is there a
  285.        proc structure count?
  286.  
  287.     *  If this is a Mach derivative, is it necessary to obtain the
  288.        task and thread structures?  How?
  289.  
  290.     *  How does one obtain the user area (or the utask area in Mach
  291.        systems) that corresponds to a process?
  292.  
  293.     *  Where are the file structures located for open file
  294.        descriptors and how are they located?  Are all file
  295.        structures in the user area?  Is the file structure space
  296.        extensible?
  297.  
  298.     *  Where do the private data pointers in file structures lead?
  299.        To gnodes?  To inodes?  To sockets?  To vnodes? Hint: look
  300.        in <sys/file.h> for DTYPE_* instances and further pointers.
  301.  
  302.     *  How are the nodes organized?  To what other nodes do they
  303.        lead and how?  Where are the common bits of information in
  304.        nodes -- device, node number, size -- stored?  Hint: look
  305.        in the header files for nodes for macroes that may be used
  306.        to obtain the address of one node from another -- e.g., the
  307.        VTOI() macro that leads from a vnode to an inode.
  308.  
  309.     *  Are text reference nodes identified and how?  Is it
  310.        necessary to examine the virtual memory map of a process or
  311.        a task to locate text references?  Some kernels have text
  312.        node pointers in the proc structures; some, in the user
  313.        area; Mach kernels may have text information in the task
  314.        structure, reached in various ways from the proc, user area,
  315.        or user task structure.
  316.  
  317.     *  How is the device table -- e.g., /dev or /devices --
  318.        organized?  How is it read?  Using direct or dirent structures?
  319.  
  320.        How are major/minor device numbers represented?  How are
  321.        device numbers assembled and disassembled?
  322.  
  323.        Are there clone devices?  How are they identified?
  324.  
  325.     *  How is mount information obtained?  Getmntinfo()?  Getmntent()?
  326.        Some special kernel call?
  327.  
  328.     *  How are sockets identified and organized?  BSD-style?  As
  329.        streams?  Are there streams?
  330.  
  331.     *  Are there special nodes -- CD-ROM nodes, FIFO nodes, etc.?
  332.  
  333.     *  How is the kernel's name cache organized?  Can lsof access
  334.        it to get partial name components?
  335.  
  336.  
  337. Dlsof.h and #include's
  338. ----------------------
  339.  
  340. Once you have identified the kernel's data organization and know
  341. what structures it provides, you must add #include's to dlsof.h to
  342. access their definitions.  Sometimes it is difficult to locate the
  343. header files -- you may need to introduce -I specifications in the
  344. Makefile via the DINC shell variable in the Configure script.
  345.  
  346. Sometimes it is necessary to define special symbols -- e.g., KERNEL,
  347. _KERNEL, _KMEMUSER -- to induce system header files to yield kernel
  348. structure definitions.  Sometimes making those symbol definitions
  349. cause other header file and definition conflicts.  There's no good
  350. general rule on how to proceed when conflicts occur.
  351.  
  352. Rarely it may be necessary to extract structure definitions from
  353. system header files and move them to dlsof.h, create special versions
  354. of system header files, or obtain special copies of system header
  355. files from "friendly" (e.g., vendor) sources.  The dlsof.h header
  356. file in lsof3/dialects/sun shows examples of the first case; the
  357. dec_a subdirectory in lsof3/dialects/osf, the second; the irix5hdr
  358. subdirectory in lsof3/dialects/sgi, a mixture of the first and
  359. third.
  360.  
  361. Building up the necessary #includes in dlsof.h is an iterative
  362. process that requires attention as you build the dialect-specific
  363. functions that references kernel structures.  Be prepared to revisit
  364. dlsof.h frequently.
  365.  
  366.  
  367. Definitions That Affect Compilation
  368. -----------------------------------
  369.  
  370. The source files at the top level contain optional functions that
  371. may be activated with definitions in a dialect's machine.h header
  372. file.  Mostly these are functions for reading node structures that
  373. may not apply to all dialects -- e.g. CD-ROM nodes (cdrnode), or
  374. `G' nodes (gnode).  Once you understand your kernel's data
  375. organization, you'll be able to decide the optional common node
  376. functions to activate.
  377.  
  378. Definitions in machine.h and dlsof.h also enable or disable other
  379. optional common features:
  380.  
  381.     HASDCACHE        enables the use of the device file cache.  It
  382.             contains information about the names, device
  383.             numbers and inode numbers of entries in the
  384.             /dev node subtree that lsof saves from call
  385.             to call.
  386.     HASCDRNODE        enables/disables readcdrnode() in node.c
  387.     HASFIFONODE        enables/disables readfifonode() in node.c
  388.     HASFSTYPE        enables/disables the use of the file system
  389.             type as reported in some stat(2) structures.
  390.     HASGNODE        enables/disables readgnode() in node.c
  391.     HASHSNODE        enables/disables readhsnode() in node.c
  392.     HASINODE        enables/disables readinode() in node.c
  393.     HASINTSIGNAL    is defined when signal() returns an int
  394.     HASKOPT        enables/disables the ability to read the
  395.             kernel's name list from a file -- e.g., from
  396.             a crash dump file.
  397.     HASMOPT        enables/disables the ability to read kernel
  398.             memory from a file -- e.g., from a crash
  399.             dump file.
  400.     HASNCACHE        enables the probing of the kernel's name cache
  401.             to obtain path name components.
  402.     HASNLIST        enables/disables nlist() function support.
  403.             (See NLIST_TYPE.)
  404.     HASPINFO        defines the name (if any) of the process
  405.             information subdirectory of the process file
  406.             system -- e.g., /proc/pinfo.  When HASPINFO is
  407.             defined, HASPROCFS should also be defined.
  408.     HASPIPENODE        enables/disables readpipenode() in node.c
  409.     HASPROCFS        defines the name (if any) of the process file
  410.             system -- e.g., /proc.  When HASPROCFS is
  411.             defined, it may also be necessary to define
  412.             HASPINFO.
  413.     HASPWSTAYOPEN    enables/disables support of a method to keep
  414.             /etc/passwd open during a sequence of lookup
  415.             operations.
  416.     HASRNODE        enables/disables readrnode() in node.c
  417.     HASSECURITY        enables/disables restricting open file
  418.             information access.
  419.     HASSTREAMS        enables/disables streams.
  420.     HASTMPNODE        enables/disables readtnode() in node.c
  421.     HASVNODE        enables/disables readvnode() function in node.c
  422.     HASXOPT        defines help text for dialect-specific X option
  423.             and enables X option processing in arg.c and main.c
  424.     HASXOPT_VALUE    defines the default binary value for the X option
  425.             in store.c
  426.     MACH        defines a MACH system.
  427.     NLIST_TYPE        is the type of the nlist table, Nl[], if it is
  428.             not nlist.  HASNLIST must be set for this
  429.             definition to be effective.
  430.     UID_ARG_T        defines the cast on a User ID when passed as
  431.             a function argument.
  432.     WARNDEVACCESS    enables the issuing of a warning message when
  433.             lsof is unable to access /dev (or /device) or
  434.             one of its subdirectories.  Some dialects (e.g.,
  435.             HP-UX) have many inaccessible subdirectories and
  436.             it is easier to inhibit the warning for them.
  437.             The -w option will also inhibit these warnings.
  438.     zeromem()        defines a macro to zero memory -- e.g., using
  439.             bzero() or memset().
  440.  
  441. Any dialect's machine.h can serve as a template for building your
  442. own.  All machine.h files usually have all definitions, disabling
  443. some (with comment prefix and suffix) and enabling others.
  444.  
  445.  
  446. Options: Common and Special
  447. ---------------------------
  448.  
  449. All but one lsof option is common; the specific option is ``-X''.
  450. If a dialect does not support a common option, the related #define
  451. in machine.h -- e.g., HASCOPT -- should be deselected.
  452.  
  453. The specific option, ``-X'', may be used by any dialect for its
  454. own purpose.  Right now (May 30, 1995) the ``-X'' option is binary
  455. (i.e., it's not allowed arguments of its own, and its value must
  456. be 0 or 1) but that could be changed should the need arise.  The
  457. option is enabled with the HASXOPT definition in machine.h; its
  458. default value is defined by HASXOPT_VALUE.
  459.  
  460. The value of HASXOPT should be the text displayed for ``-X'' by
  461. the usage() function in arg.c.  HASXOPT_VALUE should be the default
  462. value, 0 or 1.
  463.  
  464. AIX for the IBM RICS System/6000 defines the ``-X'' option to
  465. control readx() usage, since there is a bug in AIX 3.2.x kernels
  466. that readx() can expose for other processes.
  467.  
  468.  
  469. Defining Dialect-Specific Symbols and Global Storage
  470. ----------------------------------------------------
  471.  
  472. A dialect's dlsof.h and dstore.c files contain dialect-specific
  473. symbol and global storage definitions.  There are symbol definitions,
  474. for example, for function and data casts, and for file paths.
  475. Consult any dialect's dlsof.h file and convert its "Miscellaneous
  476. definitions" section to your dialect.  Dslof.h defines index symbols
  477. for the nlist() table -- X_* symbols --  when it's being used.
  478.  
  479. Global storage definitions include such things as structures for
  480. local Virtual File System (vfs) information; mount information;
  481. search file information; and kernel memory file descriptors --
  482. e.g., Kmem for /dev/kmem, Mem for /dev/mem, Swap for /dev/drum.
  483.  
  484.  
  485. Coding Dialect-specific Functions
  486. ---------------------------------
  487.  
  488. Each supported dialect must have some basic functions that the
  489. common functions of the top level may call.  Some of them may be
  490. obtained from the code fragment files in lsof3/dialects/common.
  491. Others may have to be coded specifically for the dialect.
  492.  
  493. Each supported dialect usually has private functions, too.  Those
  494. are wholly determined by the needs of the dialect's data organization
  495. and access.
  496.  
  497. These are the basic functions that each dialect must supply:
  498.  
  499.     ck_file_arg()        function to check optional file name
  500.                 arguments
  501.     initialize()        function to initialize the dialect
  502.     is_file_named()        function to check if a file was named
  503.                 by an optional file name argument
  504.     gather_proc_info()        function to gather process table
  505.                 and related information and cache it
  506.     printchdevname()        function to locate and optionally
  507.                 print the name of a character device
  508.     print_file()        function to print open file information
  509.     process_file()        function to process an open file
  510.                 structure
  511.     process_node()        function to process a primary node
  512.     process_socket()        function to process a socket
  513.     readdev() and stkdir()    functions to read and cache device
  514.                 information
  515.     readmnt()            function to read mount table information
  516.  
  517. Check the code fragments in lsof3/dialects/common and specific
  518. lsof3/dialects/* files for examples.
  519.  
  520. As you build these functions you will probably have to add #include's
  521. to dlsof.h.
  522.  
  523.  
  524. Function Prototype Definitions and the _PROTOTYPE Macro
  525. -------------------------------------------------------
  526.  
  527. Once you've defined your dialect-specific definitions, you should
  528. define their prototypes in dproto.h or locally in the file where
  529. they occur and are used.  Do this even if your compiler is not ANSI
  530. compliant -- the _PROTOTYPE macro knows how to cope with that and
  531. will avoid creating prototypes that will confuse your compiler.
  532.  
  533.  
  534. The Makefile
  535. ------------
  536.  
  537. Use an existing dialect's Makefile as a template.  Be alert for
  538. special facilities to generate header file dependencies (see the
  539. sgi Makefile).  Make sure that installation locations are appropriate.
  540. Change the GRP string accordingly.  Make sure that the install
  541. program options are correct.  Use the DEBUG string to set debugging
  542. options, like ``-g''.  You may also need to use the -O option when
  543. forking and SIGCHLD signals defeat your debugger as they do under
  544. Motorola V/88.
  545.  
  546. Finally, remember that strings can be passed from the top level's
  547. Configure shell script.  That's an appropriate way to handle options,
  548. especially if there are multiple versions of the Unix dialect to
  549. which you are porting lsof 3.
  550.  
  551.  
  552. The Mksrc Shell Script
  553. ----------------------
  554.  
  555. Pattern your Mksrc shell script after an existing one from another
  556. dialect.  Change the D shell variable to the name of your dialect's
  557. subdirectory in lsof3/dialects.  Adjust any other shell variable
  558. to your local conditions.  (Probably that won't be necessary.)
  559.  
  560. Finally, add sections to assemble your modules that use fragments
  561. from ../common.
  562.  
  563. Note that, if using symbolic links from the top level to your
  564. dialect subdirectory is impossible or impractical, you can set the
  565. MKC shell variable in Configure to something other than "ln -s" --
  566. e.g., "cp," and Configure will pass it to the Mksrc shell script
  567. in the M environment variable.
  568.  
  569.  
  570. Vic Abell <abe@cc.purdue.edu>
  571. Purdue University Computing Center
  572. July 20, 1995
  573.